require 'singleton'
require 'xmpp4r'
require 'xmpp4r/vcard'


class TunecoreAnnouncer::XmppConnection
  include Singleton

  attr_reader :server
  attr_reader :name
  attr_reader :port

  def initialize
    @logger = TunecoreAnnouncer::Logger.instance
  end

  def connect(opt)
    @options = opt
    @server = opt.jabber_server
    @name = "#{opt.name_prefix}-#{TunecoreAnnouncer::Utils.random_word(2)}"
    @password = opt.jabber_password
    admin_jids = opt.admin_jids

    register
    @logger.info "Connected to XMPP server"
    load_dictionary
    #@jabber.add(admin_jids)
    add_admins
    #@logger.debug "Added #{admin_jid} to roster"
    @logger.debug "Launching listener"
    admins(opt.hello_mesg) if opt.hello_mesg
    set_vcard
    listen
  end


   def disconnect
     #admins "Shutting down ... Sayonara!"
     deregister
     @jabber.disconnect
     @listener_thread.join
   end

  def admins(mesg)
    @options.admin_jids.each do |admin_jid|
      @jabber.deliver admin_jid, mesg
    end 
  end

  def status(presence, mesg)
    pres = Jabber::Presence::new
    set_avatar(pres) unless @options.avatar_file.nil?
    pres.set_status mesg
    @logger.debug "Setting presence: #{pres.to_s}, #{mesg}"
    @jabber.send!(pres)
  end


private

  def register
    connection_string = "#{@name}@#{@server}"
    @logger.debug "Registering #{connection_string}"
    begin
      @jabber  = Jabber::Simple.register(connection_string, @password)
    rescue => e
      @logger.fatal "XMPP connection refused: #{e.message}\n#{e.backtrace}"
      puts "XMPP connection refused: #{e.inspect}\n#{e.backtrace}"
      exit
    end
  end

  def load_dictionary
    @dictionary = TunecoreAnnouncer::CommandDictionary.new(@options.dictionary_file)
  end

  def deregister
    @logger.debug "Removing registration"
    client = @jabber.client
    client.remove_registration
  end


  def listen
    @listener_thread = Thread.new{
      @logger.debug "Listening for XMPP messages (connected=#{@jabber.connected?})"
      while @jabber.connected? do
        #@logger.debug "Polling for Jabber mesg"
        @jabber.received_messages.each do |message|
          process_incoming message
          sleep 1
        end
      end
    }
    @listener_thread.run
  end

  def process_incoming(message)
    @logger.debug "Jabber msg from #{message.from}: #{message.body}"
    command = @dictionary.lookup(message.body)
    @jabber.deliver(message.from, "I don't know #{message.body}") and return if command.nil?
    @logger.debug "Running command: #{command}"
    output = `#{command}`
    @logger.debug "Replying with #{output}"
    @jabber.deliver(message.from, output)
  end

  def set_vcard
    # set vcard info
     @avatar_sha1 = nil # this gets used later on
       vcard = Jabber::Vcard::IqVcard.new
       vcard["FN"] = @name # full name
       vcard["NICKNAME"] = @name # nickname
       # buddy icon stuff
       unless @options.avatar_file.nil?
        vcard["PHOTO/TYPE"] = "image/png"
        # open buddy icon/avatar image file
        image_file = File.new(@options.avatar_file, "r")
        # Base64 encode the file contents
        image_b64 = Base64.b64encode(image_file.read())
        # process sha1 hash of photo contents
        # this is used by the presence setting
        image_file.rewind # must rewind the file to the beginning
        @avatar_sha1 = Digest::SHA1.hexdigest(image_file.read())
        vcard["PHOTO/BINVAL"] = image_b64 # set the BINVAL to the Base64 encoded contents of our image
       end
       begin
         # create a vcard helper and immediately set the vcard info
         @logger.info "Setting Vcard"
         vcard_helper = Jabber::Vcard::Helper.new(@jabber.client).set(vcard)
       rescue
         @logger.error "Vcard operation timed out."
       end
    return true
  end

  def set_avatar(pres)
    if not @avatar_sha1.nil?
      # send the sha1 hash of the avatar to the server
      # as per the RFC http://www.xmpp.org/extensions/xep-0153.html
      x = REXML::Element::new("x")
      x.add_namespace('vcard-temp:x:update')
      photo = REXML::Element::new("photo")
      # this is the avatar hash as computed in the vcard thread above
      avatar_hash = REXML::Text.new(@avatar_sha1)
      # add text to photo
      photo.add(avatar_hash)
      # add photo to x
      x.add(photo)
      # add x to presence
      pres.add_element(x)
    end
  end

  def add_admins
    @options.admin_jids.each do |jid|
      @jabber.add jid
      @logger.debug "Added admin #{jid}"
    end
  end



end